package com.terrier.net
{
    import flash.net.Socket;
    import flash.utils.Endian;

    /**
     * Socket 读写策略对象.  可变长包头的读写策略模式
     * @author Jarvis.weng
     */
    public class WxSocketMutableHeadStrategy implements IWxSocketStrategy
    {
        private static var _defaultEndian:String = Endian.LITTLE_ENDIAN;

        /**
         * 默认的字节序
         * @return
         */
        public static function get defaultEndian():String
        {
            return _defaultEndian;
        }

        /**
         * @private
         */
        public static function set defaultEndian(value:String):void
        {
            _defaultEndian = value;
        }

        private var _readMore:Boolean = false;

        private var _endian:String = defaultEndian;

        private var _header:NetPacketHead = new NetPacketHead();

        /**
         * 构造函数
         */
        public function WxSocketMutableHeadStrategy()
        {
            // no action
        }

        /**
         * 指示数据的字节顺序；
         * 可能的值为来自 flash.utils.Endian 类的常量，Endian.BIG_ENDIAN 或 Endian.LITTLE_ENDIAN。
         * @return
         */
        public function get endian():String
        {
            return _endian;
        }

        /**
         * @private
         */
        public function set endian(value:String):void
        {
            _endian = value;
        }

        /**
         * 初始化 Socket
         * @param socket
         */
        public function initSocket(socket:Socket):void
        {
            socket.endian = endian;
        }

        /**
         * 将数据写入Socket对象
         * @param socket Socket对象
         * @param data 写入的数据
         * @return 如果需要立刻发送数据,返回true;否则,返回false
         * @throws Error 如果发送数据的字节序和Socket的字节序有冲突,抛出异常
         */
        public function writeData(socket:Socket, data:*):Boolean
        {
            var netPack:WxNetPacket = data as WxNetPacket;
            if (netPack.endian != socket.endian)
            {
                throw new Error("Endian error");
            }
            _header.writeLength(socket, netPack.length);
            socket.writeShort(netPack.id);
            socket.writeBytes(netPack);
            return true;
        }

        /**
         * 从Socket对象中读取数据
         * @param socket Socket对象
         * @return 读取的数据
         */
        public function readData(socket:Socket):*
        {
            _readMore = false;

            if (_header.dataLength == 0)
            {
                if (!_header.readLength(socket))
                {
                    return null;
                }
            }

            if (socket.bytesAvailable < _header.dataLength)
            {
                // 可读数据不完整,结束本次读取,返回空数据
                return null;
            }
            else
            {
                var data:WxNetPacket;

                if (_header.dataLength >= 2)
                {
                    data = new WxNetPacket();
                    data.endian = endian;
                    // 读取数据包id
                    data.id = socket.readUnsignedShort();
                    _header.dataLength -= 2;
                    if (_header.dataLength > 0)
                    {
                        // 读取剩余数据
                        socket.readBytes(data, 0, _header.dataLength);
                    }
                }
                // 长度头归零
                _header.dataLength = 0;
                // 检测缓冲区,如果仍有可读数据,设置标记,指示WxSocket继续读取
                _readMore = (socket.bytesAvailable > 0);
                // 返回完整数据
                return data;
            }
        }

        /**
         * 是否需要进一步读取
         * @return
         */
        public function get readMore():Boolean
        {
            return _readMore;
        }
    }
}
import flash.net.Socket;

class NetPacketHead
{
    public var longHead:Boolean;

    public var dataLength:uint;

    public function reset():void
    {

    }

    public function writeLength(socket:Socket, len:uint):void
    {
        if (len > 0xffff)
        {
            socket.writeShort(0);
            socket.writeUnsignedInt(len + 8);
        }
        else
        {
            socket.writeShort(len + 4);
        }
    }

    public function readLength(socket:Socket):Boolean
    {
        if (longHead)
        {
            return readLongHead(socket);
        }
        else
        {
            if (socket.bytesAvailable < 2)
            {
                return false;
            }
            else
            {
                var len:int = socket.readUnsignedShort();
                if (len == 0)
                {
                    longHead = true;
                    return readLongHead(socket);
                }
                dataLength = len - 2;
                return true;
            }
        }
    }

    private function readLongHead(socket:Socket):Boolean
    {
        if (socket.bytesAvailable < 4)
        {
            return false;
        }
        dataLength = socket.readUnsignedInt() - 6;
        longHead = false;
        return true;
    }
}
